﻿using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;


// Means PrefabPool
public class GameObjectPool
{
    private int mGrowth = 2;
    private string mPoolName;

    private Transform mPoolRootTrans;

    // Meta object
    private GameObject mPoolPrefab;
    private Transform mPoolTrans;

    private Stack<GameObject> mObjectStack = new Stack<GameObject>();

    /**
     * 属性
     */
    public int maxCount { get; private set; }
    public int unrecycledCount { get; private set; }
    public int availableCount { get { return mObjectStack.Count; } }

    /**
     * 构造函数
     */
    public GameObjectPool(string mPoolName, GameObject prefab, Transform parent)
        : this(mPoolName, prefab, parent, 1, 1, int.MaxValue)
    {
    }

    /**
     * 构造函数
     */
    public GameObjectPool(string mPoolName, GameObject prefab, Transform parent, int initCount)
        : this(mPoolName, prefab, parent, initCount, 1, int.MaxValue)
    {
    }

    /**
     * 构造函数
     */
    public GameObjectPool(string mPoolName, GameObject prefab, Transform parent, int initCount, int growth, int maxCount)
    {
        if (growth <= 0)
        {
            throw new ArgumentOutOfRangeException("growth must be greater than 0!");
        }
        if (maxCount < 0)
        {
            throw new ArgumentOutOfRangeException("availableItemsMaximum must be at least 0!");
        }

        this.mPoolName = mPoolName;

        this.mGrowth = growth;
        this.maxCount = maxCount;

        this.mPoolRootTrans = parent;
        this.mPoolPrefab = prefab;
        this.mPoolTrans = prefab.transform;

        //populate the parent
        mObjectStack = new Stack<GameObject>(initCount);
        BatchNewAndAdd(initCount);
    }

    public void BatchNewAndAdd (int count)
    {
        Stack<GameObject> availableItems = mObjectStack;

        int allocationCount = this.maxCount - availableItems.Count;
        if (count < allocationCount)
        {
            allocationCount = count;
        }

        for (int i = allocationCount - 1; i >= 0; i--)
        {
            AddObjectToPool(NewObjectInstance());
        }
    }

    private GameObject NewObjectInstance()
    {
        GameObject newObj = GameObject.Instantiate(mPoolPrefab, Vector3.zero, Quaternion.identity) as GameObject;
        newObj.name = GameObjectPool.GetWithoutClone(newObj.name);
        newObj.gameObject.SetActive(false);

        newObj.transform.parent = mPoolRootTrans;
        newObj.transform.localPosition = mPoolTrans.localPosition;
        newObj.transform.localRotation = mPoolTrans.localRotation;
        newObj.transform.localScale = mPoolTrans.localScale;

        return newObj;
    }

    //o(1)
    private void AddObjectToPool(GameObject go)
    {
        //add to pool
        go.SetActive(false);
        go.transform.SetParent(mPoolRootTrans, false);

        mObjectStack.Push(go);
    }

    private GameObject RemoveObjectFromPool ()
    {
        GameObject go = null;
        if (mObjectStack.Count > 0)
        {
            go = mObjectStack.Pop();
        }

        return go;
    }

    public GameObject Get()
    {
        GameObject go = RemoveObjectFromPool();
        if (null == go)
        {
            BatchNewAndAdd(this.mGrowth);
            go = RemoveObjectFromPool();
            //Debug.LogWarning("No object available & cannot grow pool: " + mPoolName);
        }

        unrecycledCount++;

        go.SetActive(true);
        return go;
    }

    //o(1)
    public void Release(string pool, GameObject po)
    {
        if (mPoolName.Equals(pool))
        {
            AddObjectToPool(po);
            unrecycledCount--;
        }
        else
        {
            Debug.LogError(string.Format("Trying to add object to incorrect pool {0} ", mPoolName));
        }

        if (unrecycledCount < 0)
        {
            Debug.LogWarning("More items recycled than obtained");
        }
    }

    public void Clear()
    {
        GameObject gobj;
        while (mObjectStack.Count > 0)
        {
            gobj = mObjectStack.Pop();
            if (null != gobj)
            {
                GameObject.Destroy(gobj);
            }
        }
    }


    static public string GetWithoutClone(string src)
    {
        if (!src.Contains("(Clone)"))
        {
            return src;
        }
        //return src.Substring(0, src.IndexOf("(Clone)"));
        return src.Replace("(Clone)", string.Empty);
    }
}
